#include "land_graphics.h"

#ifdef LAND_WINDOWS

#include <Windows.h>
#include <windowsx.h>
#include <stdio.h>

#include "GL/glew.h"

#define LAND_OGL_CORE

#include "init.h"
#include "ogl_winpc.h"


typedef struct {
    LandWindow wind;
    HWND hwnd;
    HDC hdc;
    LandWindow* next;
}OglWindow;

static struct {
    HGLRC hglrc;
    OglWindow* root;
}ctx = { 0 };


static OglWindow*
findWindow(HWND hwnd){
    OglWindow* next = ctx.root;
    while (next) {
        if (next->hwnd == hwnd) {
            return next;
        }
        next = (OglWindow*)next->next;
    }
    return NULL;
}

static LRESULT CALLBACK
wndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    OglWindow* glWin = findWindow(hwnd);
    if (!glWin) {
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }
    LandWindow* win = (LandWindow*)glWin;
    LandWindowEvent evt = { 0 };
    switch (uMsg) {
    case WM_CLOSE:
        evt.type = LAND_WINDOW_EVENT_CLOSE;
        landCloseWindow(win);
        break;
    case WM_SIZE:
        win->width = LOWORD(lParam);
        win->height = HIWORD(lParam);
        evt.type = LAND_WINDOW_EVENT_RESIZE;
        break;
    case WM_MOUSEMOVE: {
        int keyType = (int)wParam;
        int x = GET_X_LPARAM(lParam);
        int y = GET_Y_LPARAM(lParam);
        evt.type = LAND_WINDOW_EVENT_MOUSE_MOVE;
        evt.mouse.x = x;
        evt.mouse.y = y;
        break;
    }
    case WM_LBUTTONDOWN: {
        int keyType = (int)wParam;
        int x = GET_X_LPARAM(lParam);
        int y = GET_Y_LPARAM(lParam);
        evt.type = LAND_WINDOW_EVENT_MOUSE_DOWN;
        evt.mouse.x = x;
        evt.mouse.y = y;
        evt.mouse.button = LAND_MOUSE_LEFT;
        break;
    }
    case WM_LBUTTONUP: {
        int keyType = (int)wParam;
        int x = GET_X_LPARAM(lParam);
        int y = GET_Y_LPARAM(lParam);
        evt.type = LAND_WINDOW_EVENT_MOUSE_UP;
        evt.mouse.x = x;
        evt.mouse.y = y;
        evt.mouse.button = LAND_MOUSE_LEFT;
        break;
    }
    case WM_CREATE: {
        LPCREATESTRUCT st = (LPCREATESTRUCT)lParam;
        OglWindow* oglW = (OglWindow*)st->lpCreateParams;
        break;
    }
    }
    if (win->onEvent && evt.type) {
        win->onEvent(win, &evt);
    }
    return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

static int
initHdc(HDC hdc) {
    PIXELFORMATDESCRIPTOR pfd = {         /**< 设置像素描述结构 */
       sizeof(PIXELFORMATDESCRIPTOR),    /**< 像素描述结构的大小 */
       1,                                /**< 版本号 */
       PFD_DRAW_TO_WINDOW |              /**< 缓存区的输出显示在一个窗口中 */
       PFD_SUPPORT_OPENGL |              /**< 缓存区支持OpenGL绘图 */
       PFD_STEREO |                      /**< 颜色缓存区是立体缓存 */
       PFD_DOUBLEBUFFER,                 /**< 颜色缓存区是双缓存 */
       PFD_TYPE_RGBA,                    /**< 使用RGBA颜色格式 */
       16,                               /**< 颜色缓存区中颜色值所占的位深 */
       0, 0, 0, 0, 0, 0,                 /**< 使用默认的颜色设置 */
       0,                                /**< 无Alpha缓存 */
       0,                                /**< 颜色缓存区中alpha成分的移位计数 */
       0,                                /**< 无累计缓存区 */
       0, 0, 0, 0,                       /**< 累计缓存区无移位 */
       32,                               /**< 32位深度缓存 */
       0,                                /**< 无蒙版缓存 */
       0,                                /**< 无辅助缓存区 */
       PFD_MAIN_PLANE,                   /**< 必须为PFD_MAIN_PLANE，设置为主绘图层 */
       0,                                /**< 表示OpenGL实现所支持的上层或下层平面的数量 */
       0, 0, 0                           /**< 过时，已不再使用 */
    };
    int pixelFormat = ChoosePixelFormat(hdc, &pfd);
    if (!pixelFormat) {
        return 1;
    }
    if (!SetPixelFormat(hdc, pixelFormat, &pfd)) {
        return 1;
    }
    return 0;
}

static int
initContext() {
    DWORD style = WS_TILEDWINDOW;
    DWORD exStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    int sysWidth = GetSystemMetrics(SM_CXSCREEN);
    int sysHeight = GetSystemMetrics(SM_CYSCREEN);
    RECT rect = { 0, 0, 400, 400 };
    AdjustWindowRectEx(&rect, style, 0, exStyle);
    HWND hwnd = CreateWindowEx(exStyle,
        "LandGraphicsOgl", "test", style,
        0, 0, rect.right - rect.left, rect.bottom - rect.top,
        HWND_DESKTOP, 0, GetModuleHandleW(NULL), NULL);
    if (!hwnd)
        return 1;
    HDC hdc = GetDC(hwnd);
    if (!hdc) {
        DestroyWindow(hwnd);
        return 1;
    }
    if (initHdc(hdc)) {
        DestroyWindow(hwnd);
        return 1;
    }
    ctx.hglrc = wglCreateContext(hdc);
    if (!ctx.hglrc) {
        DestroyWindow(hwnd);
        return 1;
    }
    wglMakeCurrent(hdc, ctx.hglrc);
    if (glewInit() != GLEW_OK) {
        DestroyWindow(hwnd);
        return 1;
    }
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glBlendEquation(GL_FUNC_ADD);
    //DestroyWindow(hwnd);
    return 0;
}

static int
makeCurrent(LandWindow* window) {
    OglWindow* glWin = (OglWindow*)window;
    if (!wglMakeCurrent(glWin->hdc, ctx.hglrc)) {
        return 1;
    }
    return 0;
}

static size_t
sizeofWindow() {
    return sizeof(OglWindow);
}

static int
openWindow(LandWindow* window) {
    OglWindow* glWin = (OglWindow*)window;
    glWin->next = NULL;
    DWORD style = WS_TILEDWINDOW;
    DWORD exStyle = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;
    int sysWidth = GetSystemMetrics(SM_CXSCREEN);
    int sysHeight = GetSystemMetrics(SM_CYSCREEN);
    int nX = (sysWidth - window->width) / 2;
    int nY = (sysHeight - window->height) / 2;
    RECT rect = { 0, 0, window->width, window->height };
    AdjustWindowRectEx(&rect, style, 0, exStyle);
    HWND hwnd = CreateWindowEx(exStyle,
        "LandGraphicsOgl", "test", style,
        nX, nY, rect.right - rect.left, rect.bottom - rect.top,
        HWND_DESKTOP, 0, GetModuleHandleW(NULL), window);
    if (!hwnd)
        return 1;
    HDC hdc = GetDC(hwnd);
    if (!hdc) {
        DestroyWindow(hwnd);
        return 1;
    }
    if (initHdc(hdc)) {
        DestroyWindow(hwnd);
        return 1;
    }
    glWin->hwnd = hwnd;
    glWin->hdc = hdc;
    ShowWindow(hwnd, SW_NORMAL);
    wglMakeCurrent(hdc, ctx.hglrc);
    if (!ctx.root) {
        ctx.root = glWin;
    }
    else {
        ctx.root->next = window;
    }
    return 0;
}

static int
closeWindow_(LandWindow* window) {
    OglWindow* glWin = (OglWindow*)window;
    CloseWindow(glWin->hwnd);
    DestroyWindow(glWin->hwnd);
    return 0;
}

static int
setParent(LandWindow* window, LandWindow* parent) {
    OglWindow* glWin = (OglWindow*)window;
    OglWindow* oglParent= (OglWindow*)parent;
    SetParent(glWin->hwnd, oglParent->hwnd);
    return 0;
}

static int
drawCanvas(LandWindow* window, void* canvas) {
    return 0;
}
 
static int
renderWindow(LandWindow* window, LandCanvas* canvas) {
    OglWindow* glWin = (OglWindow*)window;
    LandOglCanvas* oc = (LandOglCanvas*)canvas;
    LandVector red = { 1.f, 0.f, 0.f, 1.f };
    LandVector* color = &red;
    makeCurrent(window);
    glViewport(0, 0, window->width, window->height);
    glBindFramebuffer(GL_READ_FRAMEBUFFER, oc->fboRead);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
    glBlitFramebuffer(0, 0, canvas->width, canvas->height,
        0, 0, window->width, window->height,
        GL_COLOR_BUFFER_BIT, GL_NEAREST);
    GLint err = glGetError();
    if (err) {
        printf("renderWindow err:%d\n", err);
    }
    SwapBuffers(glWin->hdc);
    return 0;
}


static LandWindowBackend windowBackend = {
    sizeofWindow,
    openWindow,
    closeWindow_,
    setParent,
    drawCanvas,
    renderWindow,
};

LandWindowBackend*
getWinpcWindowBackend() {
    return &windowBackend;
}

int
initOglWinpcContext(LandGraphics* config, LandOgl* oglConfig) {
    WNDCLASSEX wc;
    memset(&wc, 0, sizeof(WNDCLASSEX));
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wc.lpfnWndProc = wndProc;
    wc.hInstance = GetModuleHandleW(NULL);
    wc.hbrBackground = (HBRUSH)(COLOR_APPWORKSPACE);
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = "LandGraphicsOgl";
    RegisterClassEx(&wc);

    return initContext();
}

int
dispatchWinpc() {
    MSG msg;
    while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE) != 0) {
        if (msg.message != WM_QUIT) {
            DispatchMessage(&msg);
        }
        else {
            ExitThread(0);
        }
    }
    return 0;
}

#endif